use std::sync::{Arc, RwLock, mpsc::Sender, mpsc::channel};
use std::io::{BufReader, BufRead, Write};
use std::collections::HashMap;
use std::net::TcpListener;
use serde_derive::{Serialize, Deserialize};
use specs::world::Index;

use crate::sys::get_system::GetMessage;
use crate::sys::get_stream_system::GetStreamMessage;
use crate::sys::set_system::SetMessage;
use crate::sys::player_join_system::JoinMessage;
use crate::sys::spawn_system::SpawnMessage;
use crate::res::position_storage::Position;
use crate::res::velocity_storage::Velocity;
use crate::res::observing_storage::Observing;
use crate::res::minions_storage::Minions;

use crate::sys::interface::{
    QueryMinionsPositionStreamMessage,
    QueryMinionsPositionStreamSystem,
    QueryMinionsPositionMessage,
    QueryMinionsPositionSystem,
    QueryObservingPositionStreamMessage,
    QueryObservingPositionStreamSystem,
    QueryObservingPositionMessage,
    QueryObservingPositionSystem,
    QuerySelectedPositionStreamMessage,
    QuerySelectedPositionStreamSystem,
    QuerySelectedPositionMessage,
    QuerySelectedPositionSystem,
    SetVelocityMessage,
    SetVelocitySystem,
};

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Target {
    Selected(Vec<Index>),
    Minions,
    Observing,
}

#[derive(Serialize, Deserialize, Debug, PartialEq)]
enum Command {
    Join { name: String },
    Spawn { x: f32, y: f32 },

    Position { target: Target },
    PositionStream { target: Target },
    Move { id: Index, x: f32, y: f32 },
}

pub fn handle_connections(
    query_selected_position_sn: Sender<QuerySelectedPositionMessage>,
    query_selected_position_stream_sn: Sender<QuerySelectedPositionStreamMessage>,
    query_minions_position_sn: Sender<QueryMinionsPositionMessage>,
    query_minions_position_stream_sn: Sender<QueryMinionsPositionStreamMessage>,
    query_observing_position_sn: Sender<QueryObservingPositionMessage>,
    query_observing_position_stream_sn: Sender<QueryObservingPositionStreamMessage>,
    set_velocity_sn: Sender<SetVelocityMessage>,

    join_sn: Sender<JoinMessage>,
    spawn_sn: Sender<SpawnMessage>,
) {
    let listener = TcpListener::bind("127.0.0.1:1337").unwrap();
    let players = Arc::new(RwLock::new(HashMap::<String, Index>::new()));

    for stream in listener.incoming() {
        let query_selected_position_sn = query_selected_position_sn.clone();
        let query_selected_position_stream_sn = query_selected_position_stream_sn.clone();
        let query_minions_position_sn = query_minions_position_sn.clone();
        let query_minions_position_stream_sn = query_minions_position_stream_sn.clone();
        let query_observing_position_sn = query_observing_position_sn.clone();
        let query_observing_position_stream_sn = query_observing_position_stream_sn.clone();
        let set_velocity_sn = set_velocity_sn.clone();
        let join_sn = join_sn.clone();
        let spawn_sn = spawn_sn.clone();

        let players = players.clone();
        std::thread::spawn(move || {
            if let Ok(stream) = stream {
                let mut reader_stream = BufReader::new(stream.try_clone().unwrap());
                let mut writer_stream = stream;

                let mut player_name = String::new();
                reader_stream.read_line(&mut player_name).unwrap();
                player_name = player_name.trim_end_matches("\n").to_string();

                let player = {
                    let map = players.read().unwrap();

                    if let Some(id) = map.get(&player_name) {
                        Some(id.clone())
                    } else {
                        None
                    }
                };

                let mut data = String::new();
                reader_stream.read_line(&mut data).unwrap();

                if let Ok(cmd) = serde_json::from_str(&data) {
                    match cmd {
                        Command::Join { name } => {
                            let (sd, rc) = channel();

                            let join_data = JoinMessage::new(sd);
                            join_sn.send(join_data).unwrap();

                            if let Ok(player_id) = rc.recv() {
                                let mut map = players.write().unwrap();
                                map.insert(name, player_id);
                            }
                        },
                        Command::Spawn { x, y } => {
                            if let Some(player) = player {
                                let (sd, rc) = channel();

                                let spawn_data = SpawnMessage::new(x, y, player.clone(), sd);
                                spawn_sn.send(spawn_data).unwrap();

                                if let Ok(entity_id) = rc.recv() {
                                    writer_stream.write(entity_id.to_string().as_bytes()).unwrap();
                                    writer_stream.flush().unwrap();
                                }
                            }
                        },
                        Command::Position { target } => {
                            if let Some(player) = player {
                                let (sd, rc) = channel();

                                match target {
                                    Target::Selected(targets) => {
                                        let message = QuerySelectedPositionMessage::new(targets, sd, player);
                                        query_selected_position_sn.send(message).unwrap();
                                    },
                                    Target::Minions => {
                                        let message = QueryMinionsPositionMessage::new(sd, player);
                                        query_minions_position_sn.send(message).unwrap();
                                    },
                                    Target::Observing => {
                                        let message = QueryObservingPositionMessage::new(sd, player);
                                        query_observing_position_sn.send(message).unwrap();
                                    }
                                }

                                while let Ok(received) = rc.recv() {
                                    let mut map = HashMap::new();
                                    for r in received.iter() {
                                        let r = r.clone();
                                        map.insert(r.0.to_string(), r.1);
                                    }

                                    let mut bytes = serde_json::to_vec(&map).unwrap();
                                    bytes.push('\n' as u8);

                                    if let Err(_) = writer_stream.write(&bytes) {
                                        break;
                                    }
                                    writer_stream.flush().unwrap();
                                }
                            }
                        },
                        Command::PositionStream { target } => {
                            if let Some(player) = player {
                                let (sd, rc) = channel();

                                match target {
                                    Target::Selected(targets) => {
                                        let message = QuerySelectedPositionStreamMessage::new(targets, sd, player);
                                        query_selected_position_stream_sn.send(message).unwrap();
                                    },
                                    Target::Minions => {
                                        let message = QueryMinionsPositionStreamMessage::new(sd, player);
                                        query_minions_position_stream_sn.send(message).unwrap();
                                    },
                                    Target::Observing => {
                                        let message = QueryObservingPositionStreamMessage::new(sd, player);
                                        query_observing_position_stream_sn.send(message).unwrap();
                                    }
                                };

                                while let Ok(received) = rc.recv() {
                                    let mut map = HashMap::new();
                                    for r in received.iter() {
                                        let r = r.clone();
                                        map.insert(r.0.to_string(), r.1);
                                    }

                                    let mut bytes = serde_json::to_vec(&map).unwrap();
                                    bytes.push('\n' as u8);

                                    if let Err(_) = writer_stream.write(&bytes) {
                                        break;
                                    }
                                    writer_stream.flush().unwrap();
                                }
                            }
                        },
                        Command::Move { id, x, y } => {
                            if let Some(player) = player {
                                let velocity = Velocity{x,y};
                                let message = SetVelocityMessage::new(id, velocity, player);
                                set_velocity_sn.send(message).unwrap();
                            }
                        },
                    }
                }

                println!("{:?}", players);
            }
        });
    }
}

fn evaluate_target(
    target: Target,
    player: Index,
    observe_sn: Sender<GetMessage<Observing>>,
    minions_sn: Sender<GetMessage<Minions>>,
) -> Vec<Index> {
    return match target {
        Target::Selected(array) => array,
        Target::Observing => {
            let (sd, rc) = channel();

            let observe_data = GetMessage::<Observing>::new([player.clone()].to_vec(), sd, player.clone());
            observe_sn.send(observe_data).unwrap();

            if let Ok(observing) = rc.recv() {
                observing.get(0).unwrap().0.clone()
            } else {
                [].to_vec()
            }
        },
        Target::Minions => {
            let (sd, rc) = channel();

            let own_data = GetMessage::<Minions>::new([player.clone()].to_vec(), sd, player.clone());
            minions_sn.send(own_data).unwrap();

            if let Ok(owning) = rc.recv() {
                owning.get(0).unwrap().0.clone()
            } else {
                [].to_vec()
            }
        }
    };
}